from tools import *
from models import *


class BaseStrategy:
    def __init__(self, operation_list:List[List[Operation]], vm_type:Dict[str,Vmtype], server_type:List[Servertype]):
        pass




def return_top10_cost_performance_server(server_type):      # TODO 缓存排序结果， 加快程序运行
    max = 0
    max_index_list = []
    index = 0
    re_list = []
    # print(len(server_type))
    for single_server in server_type:
        cost_p = single_server.cost_performance()
        # print(cost_p)
        if (cost_p >= max):
            max = cost_p
            max_index = index
            # print(max_index)
            max_index_list.append(max_index)
        else:
            index += 1
    # print(len(max_index_list))
    for i in max_index_list:
        re_list.append(server_type[i])
    # print(max_index_list[-10:-1])
    return re_list

class Strategy:
    """
    代表一种处理策略的类
    """
    def __init__(self, operation_list:List[List[Operation]], vm_type:Dict[str,Vmtype], server_type:List[Servertype]):
        self.operation_list = operation_list
        self.vm_type_dict = vm_type
        self.server_type = server_type
        self.server_type_dict = {t.name: t for t in server_type}
        self.server_list: List[ServerInstance] = [] # 存储物理机实例
        self.server_dict: Dict[int, ServerInstance] = dict() # 物理机id -> 物理机实例
        self.vm_dict: Dict[str, Tuple[Vmtype, ServerInstance]] = dict()
        self.cnt = 0

        # 按照性价比对服务器类型排序，降序排序
        self.sorted_server_type_list_james = return_top10_cost_performance_server(server_type)
        self.ordered_server_type_list = [t for _,t in sorted(map(lambda st:(-st.cost_performance(), st), self.server_type))]

    def run(self, output:Output):
        self.result = output
        self.process()

    def process(self):
        """处理数据，保存结果"""
        for day, operation_today in enumerate(self.operation_list):
            # 处理一天的数据
            # 扫描两遍，为了给服务器编号

            op_list: List[Tuple[Vmtype, ServerInstance, str]] = []     # 存储(虚拟机类型, 虚拟机所在的物理服务器, 虚拟机再服务器上哪个结点)
            new_instance = {}    # 统计购买的各种服务器数量 str -> list[ServerInstance]
            # 第一遍扫描产生一个列表
            for operation in operation_today:
                if operation.mode == Operation.Op.ADD:
                    # 尝试创建一个实例并且放入物理机中
                    vmtype = self.vm_type_dict[operation.vm_type_id]
                    instance = self.find_first_fit_for_vm(vmtype)
                    if instance is None:
                        # 找不到能放下的物理机，那就买一台
                        #instance = self.purchase_server_that_fit_vm(vmtype)
                        instance = self.purchase_server_james_edition(vmtype)
                        if instance.type.name in new_instance:
                            new_instance[instance.type.name].append(instance)
                        else:
                            new_instance[instance.type.name] = [instance]
                    node = instance.allocate(vmtype, operation.vmid)
                    op_list.append((vmtype, instance, node))
                    self.vm_dict[operation.vmid] = (vmtype, instance)
                else:
                    # 删除虚拟机
                    vmtype, instance = self.vm_dict[operation.vmid]
                    instance.free(vmtype, operation.vmid)
                    self.vm_dict.pop(operation.vmid)
            # 给没有编号的所有新增instance编号
            for name, instance_list in new_instance.items():
                self.result.purchase_server(self.server_type_dict[name], len(instance_list))
                for inst in instance_list:
                    inst.server_id = ServerInstance.next_index()
                    #result.purchase_server(inst.type)

            # 第二次扫描
            # 输出所有的add操作结果
            for vmtype, instance, node in op_list:
                if vmtype.is_double:
                    self.result.allocate(instance.server_id)
                else:
                    self.result.allocate_single_node(instance.server_id, node)

            # 遍历所有服务器，统计占用信息
            total_core = 0
            total_memory = 0
            used_core = 0
            used_memory = 0
            for i in self.server_list:
                total_core += i.type.core
                total_memory += i.type.memory
                used_core += i.a_core_remain
                used_core += i.b_core_remain
                used_memory += i.a_memory_remain
                used_memory += i.b_memory_remain
            l.info('resource used: Mem(%.2f%%), CPU(%.2f%%)' % (used_memory / total_memory * 100, used_core / total_core * 100))

            l.debug('done processing day %d' % (day+1))
            #self.result.output_today()
            self.result.start_a_new_day()

    def purchase_server_james_edition(self, vmtype:Vmtype) -> ServerInstance:
        # 满足条件的服务器
        # 满足条件的服务器
        import random
        stype_list = self.sorted_server_type_list_james
        stype = random.choice(stype_list)
        inst = ServerInstance(stype, None)
        # for stype in stype_list:
        # inst = ServerInstance(stype, None)
        # if inst.can_fit(vmtype):
        # self.cnt += 1
        # instance = ServerInstance.get_instance(stype)
        # self.server_list.append(instance)
        # return instance
        if inst.can_fit(vmtype):
            self.cnt += 1
            instance = ServerInstance.get_instance(stype)
            self.server_list.append(instance)
            return instance
        for stype in self.server_type:
            inst = ServerInstance(stype, None)
            if inst.can_fit(vmtype):
                self.cnt += 1
                instance = ServerInstance.get_instance(stype)
                self.server_list.append(instance)
                return instance


    def purchase_server_that_fit_vm(self, vmtype:Vmtype) -> ServerInstance:
        # 满足条件的服务器
        import random
        attempt_times = 10
        for _ in range(attempt_times):
            stype = random.choice(self.server_type)
            inst = ServerInstance(stype, None)
            if inst.can_fit(vmtype):
                self.cnt += 1
                instance = ServerInstance.get_instance(stype)
                self.server_list.append(instance)
                return instance
        for stype in self.server_type:
            inst = ServerInstance(stype, None)
            if inst.can_fit(vmtype):
                self.cnt += 1
                instance = ServerInstance.get_instance(stype)
                self.server_list.append(instance)
                return instance
        """
        hostChoose_list = []
        minhardware_price = 9999999
        bestServer = server_type_list[0]
        for item in server_type_list:
            if item.core > core and item.memory > memory:
                hostChoose_list.append(item)
                if (item.hardware_price < minhardware_price):
                    minhardware_price = item.hardware_price
                    bestServer = item
        instance = ServerInstance.get_instance(bestServer)
        self.result.purchase_server(bestServer)
        self.server_list.append(instance)
        self.server_dict[instance.server_id] = instance
        """
        sys.exit(-1)
        return instance
        #return hostChoose_list, bestServer

    def find_first_fit_for_vm(self, vmtype) -> ServerInstance:
        """分配虚拟机"""
        # 遍历物理机实例列表，查找第一个能放下的
        attempt_threshold = 10
        if len(self.server_list) == 0: return None
        import random
        for i in range(attempt_threshold):
            instance = random.choice(self.server_list)
            if instance.can_fit(vmtype):
                return instance
        return None
        for server_instance in self.server_list:
            if server_instance.can_fit(vmtype):
                return server_instance
        return None

